﻿using Newtonsoft.Json.Linq;
using RulesEngine.Models;
using System.Text;

namespace 鸡兔同笼;

public static class RuleExtensions
{
    public static void PreProcess(this Rule rule, Workflow workflow, Rule parentRule = null)
    {
        if (!string.IsNullOrWhiteSpace(parentRule?.Expression))
        {
            if (!string.IsNullOrWhiteSpace(rule.Expression))
            {
                rule.Expression = $"({parentRule.Expression}) && ({rule.Expression})";
            }
            else
            {
                rule.Expression = parentRule.Expression;
            }
        }

        if (rule.Rules != null && rule.Rules.Count() > 1)
        {
            var parameterName = $"p{Guid.NewGuid():N}";

            if (rule.Operator == "And")
            {
                ((List<ScopedParam>)rule.LocalParams).Add(new LocalParam
                {
                    Name = parameterName,
                    Expression = "new{true as IsContinue}"
                });

                foreach (var childRule in rule.Rules)
                {
                    var expression = $"({parameterName}.IsContinue && ({childRule.Expression}))";
                    childRule.Expression = $"({expression} || !Utils.SetValue({parameterName},\"IsContinue\",false))";
                }
            }
            else if (rule.Operator == "Or")
            {
                if (rule.Properties.TryGetValue("nestedRuleOutputMode", out object nestedRuleOutputModeValue))
                {
                    if (nestedRuleOutputModeValue?.ToString() == "one")
                    {
                        ((List<ScopedParam>)rule.LocalParams).Add(new LocalParam
                        {
                            Name = parameterName,
                            Expression = "new{true as IsContinue}"
                        });

                        foreach (var childRule in rule.Rules)
                        {
                            var expression = $"({parameterName}.IsContinue && ({childRule.Expression}))";
                            childRule.Expression = $"({expression} && Utils.SetValue({parameterName},\"IsContinue\",false))";
                        }
                    }
                }
            }
        }

        if (rule.Properties != null)
        {
            if (rule.Properties.TryGetValue("assignments", out object assignmentsValue) && assignmentsValue is JArray assignments)
            {
                var expression = new StringBuilder();
                foreach (var assignment in assignments.OfType<JObject>())
                {
                    var targetObject = assignment.GetValue("TargetObject")?.Value<string>();
                    var propertyName = assignment.GetValue("PropertyName")?.Value<string>();
                    var propertyValue = assignment.GetValue("PropertyValue")?.Value<string>();
                    if (!string.IsNullOrWhiteSpace(targetObject)
                        && !string.IsNullOrWhiteSpace(propertyName)
                        && !string.IsNullOrWhiteSpace(propertyValue))
                    {
                        expression.Append($"(Utils.SetValue({targetObject},\"{propertyName}\",({propertyValue})))&&");
                    }
                }

                if (expression.Length > 0)
                {
                    expression.Remove(expression.Length - 2, 2);
                }

                rule.Rules ??= new List<Rule>();

                if (!rule.Rules.Any())
                {
                    rule.Operator = "And";
                }

                var assignmentRule = new Rule
                {
                    RuleName = Guid.NewGuid().ToString(),
                    Expression = expression.ToString(),
                };

                ((IList<Rule>)rule.Rules).Insert(0, assignmentRule);
            }

            if (rule.Properties.TryGetValue("loopEnabled", out object loopEnabledValue) && loopEnabledValue is bool loopEnabled && loopEnabled)
            {
                if (!string.IsNullOrWhiteSpace(rule.Expression) && workflow.Rules.Contains(rule))
                {
                    rule.Rules ??= new List<Rule>();

                    if (!rule.Rules.Any())
                    {
                        rule.Operator = "And";
                    }

                    ((List<Rule>)rule.Rules).Add(new Rule
                    {
                        RuleName = Guid.NewGuid().ToString(),
                        SuccessEvent = rule.SuccessEvent,
                        Actions = new RuleActions
                        {
                            OnSuccess = new ActionInfo
                            {
                                Name = "InvokeRule",
                                Context = new Dictionary<string, object>
                                {
                                    { "workflowName", workflow.WorkflowName },
                                    { "ruleName", rule.RuleName }
                                }
                            }
                        }
                    });
                }
            }
        }

        if (rule.Rules != null)
        {
            foreach (var childRule in rule.Rules.ToList())
            {
                PreProcess(childRule, workflow, rule);
            }
        }
    }
}
